// 防抖
function debounce(fn, delay) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn(...args);
    }, delay);
  };
}
// 只执行第一次或只执行最后一次
function debounce2(fn, delay, imm = false) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    if (!timer && imm) fn(...args);

    timer = setTimeout(() => {
      if (!imm) fn(...args);
      timer = null;
    }, delay);
  };
}

// 节流 间隔时间内只执行一次
function throttle(fn, delay) {
  let timer;
  return (...args) => {
    if (timer) return;
    fn(...args);
    timer = setTimeout(() => {
      timer = null;
    }, delay);
  };
}
function throttle2(fn, delay) {
  let timer;
  return (...args) => {
    if (timer) return;
    fn(...args);
    timer = setTimeout(() => {
      timer = null;
    }, delay);
  };
}
// 深克隆
function deepClone(obj, hash = new WeakMap()) {
  // 递归时基础类型返回
  if (typeof obj != "object" || obj === null) return obj;
  if (hash.has(obj)) return hash.get(obj);

  const newObj = Array.isArray(obj) ? [] : {};
  hash.set(obj, newObj);

  Object.keys(obj).forEach((key) => {
    newObj[key] = deepClone(obj[key], hash);
  });
  return newObj;
}
function deepClone2(obj, hash = new WeakMap()) {
  if (typeof obj !== "object" || obj === null) return obj;
  if (hash.has(obj)) return hash.get(obj);

  const newObj = Array.isArray(obj) ? [] : {};
  hash.set(obj, newObj);

  Object.keys(obj).forEach((key) => {
    newObj[key] = deepClone2(obj[key], hash);
  });

  return newObj;
}

// call、apply、bind的实现
function myCall(context = globalThis, ...args) {
  const fn = Symbol("fn");
  context[fn] = this;
  const res = context[fn](...args);
  delete context[fn];
  return res;
}
function myApply(context = globalThis, args = []) {
  const fn = Symbol("fn");
  context[fn] = this;
  const res = context[fn](...args);
  delete context[fn];
  return res;
}
function myBind(context = globalThis, ...bindArgs) {
  const fn = this;
  return function (...args) {
    return fn.apply(context, [...bindArgs, ...args]);
  };
}

function myCall2(context = globalThis, ...args) {
  const fn = Symbol("fn");
  context[fn] = this;
  const res = context[fn](...args);
  delete context[fn];
  return res;
}
function myBind2(context = globalThis, ...bindArgs) {
  const fn = this;
  return function (...args) {
    return fn.apply(context, [...bindArgs, ...args]);
  };
}

// 实现Promise.all
function myAll(promise: Promise<any>[]) {
  return new Promise((resolve, reject) => {
    if (!promise?.length) return resolve([]);

    const result: any[] = [];
    let completed = 0;
    promise.forEach((p, i) => {
      Promise.resolve(p).then(
        (res) => {
          result[i] = res;
          completed++;
          if (completed === promise.length) resolve(result);
        },
        (err) => reject(err)
      );
    });
  });
}
function myAll2(promise: Promise<any>[]) {
  if (!promise.length) return [];
  return new Promise((resolve, reject) => {
    const result: any[] = [];
    let complete = 0;

    promise.forEach((p, i) => {
      Promise.resolve(p).then(
        (res) => {
          result[i] = res;
          complete++;
          if (complete === result.length) resolve(result);
        },
        (err) => {
          reject(err);
        }
      );
    });
  });
}

// 实现new操作符
function New(fn: Function, ...args: never) {
  const obj = Object.create(fn.prototype || Object.prototype);
  const res = fn.apply(obj, args);
  const isObj =
    res !== null && (typeof obj === "object" || typeof obj === "function");
  return isObj ? res : obj;
}
function myNew(construct: Function, ...args: never[]) {
  if (typeof construct !== "function")
    throw new TypeError("myNew: 第一个参数必须是构造函数");

  // 原型链指向构造函数的原型
  const obj = Object.create(construct.prototype || Object.prototype);
  // 绑定this执行构造函数
  const res = construct.apply(obj, args);
  // 3. 如果构造函数显式返回了一个对象（或函数），则返回它；
  const isObj = res && (typeof res === "object" || typeof res === "function");
  return isObj ? res : obj;
}
// 简单实现
function myNew1(construct: Function, ...args: never[]) {
  return Reflect.construct(construct, args);
}
function myNew2(construct: Function, ...args: never[]) {
  // 原型链指向构造函数的原型
  const obj = Object.create(construct.prototype);
  // 绑定this执行构造函数
  const res = construct.apply(obj, args);

  const isObj = res && (typeof res === "object" || typeof res === "function");

  return isObj ? res : obj;
}

// 实现简单的订阅发布
class EventEmitter {
  private events: Map<string, Function[]> = new Map();

  on(event: string, listener: (...args: never[]) => unknown) {
    const cbs = this.events.get(event) || [];
    this.events.set(event, [...cbs, listener]);
    return this;
  }
  off(event: string, listener: (...args: never[]) => unknown) {
    const cbs = this.events.get(event) || [];
    if (listener) {
      this.events.set(
        event,
        cbs.filter((fn) => fn !== listener)
      );
    } else {
      this.events.delete(event);
    }

    return this;
  }
  emit(event: string, ...args: never[]) {
    const cbs = this.events.get(event) || [];
    cbs.forEach((listener) => listener(...args));
  }
}

class Event2 {
  private events = new Map<string, ((...args: never[]) => unknown)[]>();

  on(event: string, cb: (...args: never[]) => unknown) {
    const cbs = this.events.get(event) || [];
    this.events.set(event, [...cbs, cb]);
  }
  emit(event: string, ...args: never[]) {
    const cbs = this.events.get(event) || [];
    cbs.forEach((cb) => {
      cb(...args);
    });
  }
  off(event: string, cb?: (...args: never[]) => unknown) {
    const cbs = this.events.get(event) || [];

    if (cb) {
      this.events.set(
        event,
        cbs.filter((fn) => fn !== cb)
      );
    } else {
      this.events.delete(event);
    }
  }
}

// 实现受控的input组件
// react中完全获取input的改变后的值，和任何输入的值，
// 出入口都处于掌握中，称之为完全受控

// instanceof 的实现
function myInstanceof(instance, construct) {
  if (typeof construct !== "function")
    throw new TypeError("Right-hand side of 'instanceof' is not callable");

  if (
    instance === null ||
    (typeof instance != "object" && typeof instance != "function")
  )
    return false;

  let proto = Reflect.getPrototypeOf(instance);
  const prototype = construct.prototype;

  while (proto) {
    if (proto === prototype) return true;
    proto == Reflect.getPrototypeOf(proto);
  }
  return false;
}

function myInstanceof2(instance, construct) {
  if (
    (typeof instance !== "object" && typeof instance !== "function") ||
    instance === null
  )
    return false;

  let proto = Reflect.getPrototypeOf(instance);
  const prototype = construct.prototype;
  while (proto) {
    if (proto === prototype) return true;
    proto = Reflect.getPrototypeOf(proto);
  }
  return false;
}

// 实现一个简单的 LRU Cache
// 实现简单的 LRU Cache
// lru 最近最少使用缓存
class LruCache {
  private map = new Map();
  private size = 0;
  constructor(size) {
    this.size = size;
  }

  put(key: string, val) {
    if (this.map.has(key)) {
      this.map.delete(key);
    } else {
      if (this.map.size >= this.size) {
        const oldestKey = this.map.keys().next().value;
        this.map.delete(oldestKey);
      }
    }
    this.map.set(key, val);
  }
  get(key: string) {
    if (!this.map.has(key)) return;
    const val = this.map.get(key);
    // 刷新key
    this.map.delete(key);
    this.map.set(key, val);
    return val;
  }
}
// const cache = new LruCache2(10);
// console.log(cache.get("0"));

// 获取最近5分钟的敲击次数，只接受递增时间
class HitCounter {
  private map = new Map<number, number>();
  private size = 300;
  private totalHits = 0;

  hit(time: number) {
    this.deleteOver(time);
    const lastHits = this.map.get(time) || 0;
    this.map.set(time, lastHits + 1);
    this.totalHits++;
  }

  getHits(time: number) {
    this.deleteOver(time);
    this.totalHits;
  }

  deleteOver(time: number) {
    const oldestTime = this.map.keys().next().value;
    if (time - oldestTime >= this.size) {
      this.totalHits -= this.map.get(oldestTime)!;
      this.map.delete(oldestTime);
    }
  }
}

function getDayView(input: string) {
  const arr = input.split("\n").filter(Boolean);
  arr.shift();
  const map = new Map<string, Set<string>>();
  const sumSet = new Set();
  for (const str of arr) {
    const log = str.split("|").map((str) => str.trim());
    const [date, ip, path, status] = log;
    if (status !== "success" || path !== "/login.do") continue;
    const IP = ip
      .split(".")
      .map((str) => +str)
      .join(".");
    const set = map.get(date) || new Set();
    sumSet.add(IP);
    set.add(IP);
    map.set(date, set);
  }
  const res = new Array(32).fill(0);
  res[0] = sumSet.size;
  for (let [key, set] of map) {
    res[+key.split("-")[2]] = set.size;
  }
  return res.join(" ");
}
